#!/usr/bin/env python3
"""
日志分析工具
支持递归解压多种格式压缩包，按规则过滤日志文件
"""

import os
import sys
import shutil
import zipfile
import tarfile
import re
import json
import fnmatch
from pathlib import Path
from typing import List, Dict, Set


class LogAnalyzer:
    """日志分析工具主类"""
    
    def __init__(self, config_file: str = "log_rules.json"):
        self.config_file = config_file
        self.rules = []
        self.extracted_files = []
        self.load_rules()
    
    def load_rules(self):
        """加载过滤规则"""
        try:
            if os.path.exists(self.config_file):
                with open(self.config_file, 'r', encoding='utf-8') as f:
                    self.rules = json.load(f)
            else:
                # 默认规则
                self.rules = [
                    {
                        "name": "错误日志",
                        "pattern": "ERROR|Error|error"
                    },
                    {
                        "name": "警告日志",
                        "pattern": "WARN|Warning|warning"
                    },
                    {
                        "name": "异常日志",
                        "pattern": "Exception|exception"
                    }
                ]
                self.save_rules()
        except Exception as e:
            print(f"加载规则失败: {e}")
            self.rules = []
    
    def save_rules(self):
        """保存过滤规则"""
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(self.rules, f, ensure_ascii=False, indent=2)
        except Exception as e:
            print(f"保存规则失败: {e}")
    
    def add_rule(self, name: str, pattern: str):
        """添加新规则"""
        rule = {"name": name, "pattern": pattern}
        self.rules.append(rule)
        self.save_rules()
    
    def delete_rule(self, index: int):
        """删除规则"""
        if 0 <= index < len(self.rules):
            del self.rules[index]
            self.save_rules()
    
    def update_rule(self, index: int, name: str, pattern: str):
        """更新规则"""
        if 0 <= index < len(self.rules):
            self.rules[index] = {"name": name, "pattern": pattern}
            self.save_rules()
    
    def list_rules(self):
        """列出所有规则"""
        return self.rules
    
    def extract_archive(self, archive_path: str, extract_to: str) -> List[str]:
        """
        递归解压压缩包，支持 zip, tar.gz, 7z 格式
        返回解压出的所有文件路径
        """
        extracted = []
        archive_path = os.path.abspath(archive_path)
        
        try:
            if zipfile.is_zipfile(archive_path):
                with zipfile.ZipFile(archive_path, 'r') as zip_ref:
                    zip_ref.extractall(extract_to)
                    for name in zip_ref.namelist():
                        extracted_path = os.path.join(extract_to, name)
                        extracted.append(extracted_path)
                        # 递归解压
                        if self.is_archive(extracted_path):
                            sub_extract_to = os.path.join(extract_to, os.path.splitext(name)[0])
                            os.makedirs(sub_extract_to, exist_ok=True)
                            extracted.extend(self.extract_archive(extracted_path, sub_extract_to))
            
            elif tarfile.is_tarfile(archive_path):
                with tarfile.open(archive_path, 'r') as tar_ref:
                    tar_ref.extractall(extract_to)
                    for member in tar_ref.getmembers():
                        extracted_path = os.path.join(extract_to, member.name)
                        extracted.append(extracted_path)
                        # 递归解压
                        if member.isfile() and self.is_archive(extracted_path):
                            sub_extract_to = os.path.join(extract_to, os.path.splitext(member.name)[0])
                            os.makedirs(sub_extract_to, exist_ok=True)
                            extracted.extend(self.extract_archive(extracted_path, sub_extract_to))
            
            elif archive_path.endswith('.7z'):
                # 需要安装 py7zr: pip install py7zr
                try:
                    import py7zr
                    with py7zr.SevenZipFile(archive_path, mode='r') as z:
                        z.extractall(extract_to)
                        for name in z.getnames():
                            extracted_path = os.path.join(extract_to, name)
                            extracted.append(extracted_path)
                            # 递归解压
                            if self.is_archive(extracted_path):
                                sub_extract_to = os.path.join(extract_to, os.path.splitext(name)[0])
                                os.makedirs(sub_extract_to, exist_ok=True)
                                extracted.extend(self.extract_archive(extracted_path, sub_extract_to))
                except ImportError:
                    print("请安装 py7zr 库以支持 7z 格式: pip install py7zr")
                    
        except Exception as e:
            print(f"解压文件 {archive_path} 失败: {e}")
        
        return extracted
    
    def is_archive(self, file_path: str) -> bool:
        """判断是否为支持的压缩文件格式"""
        if not os.path.isfile(file_path):
            return False
        
        return (file_path.endswith(('.zip', '.tar.gz', '.tgz', '.tar.bz2', '.tbz2', '.7z')) or 
                zipfile.is_zipfile(file_path) or 
                tarfile.is_tarfile(file_path))
    
    def filter_log_files(self, directory: str, pattern: str = "*.log") -> List[str]:
        """根据文件名模式筛选日志文件"""
        log_files = []
        for root, _, files in os.walk(directory):
            for file in files:
                if fnmatch.fnmatch(file, pattern):
                    log_files.append(os.path.join(root, file))
        return log_files
    
    def copy_files_to_directory(self, files: List[str], destination: str):
        """复制文件到指定目录"""
        os.makedirs(destination, exist_ok=True)
        for file in files:
            try:
                dest_file = os.path.join(destination, os.path.basename(file))
                shutil.copy2(file, dest_file)
            except Exception as e:
                print(f"复制文件 {file} 失败: {e}")
    
    def filter_logs_by_rule(self, log_files: List[str], rule_index: int) -> Dict[str, List[str]]:
        """
        根据指定规则过滤日志文件
        返回每个文件中匹配的行
        """
        if rule_index >= len(self.rules):
            return {}
        
        pattern = self.rules[rule_index]["pattern"]
        compiled_pattern = re.compile(pattern)
        result = {}
        
        for log_file in log_files:
            matched_lines = []
            try:
                with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
                    for line_num, line in enumerate(f, 1):
                        if compiled_pattern.search(line):
                            matched_lines.append(f"{line_num}: {line.rstrip()}")
                
                if matched_lines:
                    result[log_file] = matched_lines
                    
            except Exception as e:
                print(f"读取文件 {log_file} 失败: {e}")
        
        return result


if __name__ == "__main__":
    # 创建示例程序
    analyzer = LogAnalyzer()
    print("日志分析工具已启动")
    print("可用规则:")
    for i, rule in enumerate(analyzer.list_rules()):
        print(f"{i+1}. {rule['name']}: {rule['pattern']}")